<?php
/* this file stores all of the functions that are used
over all of the other files. */

ini_set("log_errors", 1);
$srv = $user_settings['instance'];
//$token = ($token != false ? msc($token,'d') : false);
$token = ($token != false ? $token : false);






// FUNCTIONS THAT HAVE TO DO WITH INSTANCE COMMUNICATION AND RETRIEVAL

/* a function to make an authenticated general GET api call to the logged-in instance.
   - $url is a string of an api call like "account/:id/statuses" 
   
   - returns the array conversion of the json response
 */
function api_get($url) {
	global $srv;
	global $token;

	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/" . $url);
	if (!is_null($token)) {
		curl_setopt($curl, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
	}
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	$result = curl_exec($curl);
	curl_close($curl);
	return json_decode($result, true);
}

/* same as above but used in some newer api endpoinds (v2) */
function api_getv2($url) {
	global $srv;
	global $token;

	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v2/" . $url);
	if (!is_null($token)) {
		curl_setopt($curl, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
	}
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	$result = curl_exec($curl);
	curl_close($curl);

	return json_decode($result, true);
}

/* a function to make an authenticated general POST api call to the logged-in instance.
   - $url is a string of an api call like "account/:id/statuses" 
   
   - returns the array conversion of the json response
 */
function api_post($url, $array) {
	global $srv;
	global $token;

	$cSession = curl_init();
	curl_setopt($cSession, CURLOPT_HEADER, false);
	curl_setopt($cSession, CURLOPT_POST, 1);
	curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/" . $url);
	if (!is_null($token)) {
		curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
	}
	curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query($array));
	curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
	$result = curl_exec($cSession);
	curl_close($cSession);

	return json_decode($result, true);
}

/* a function to make an authenticated general DELETE api call to the logged-in instance.
   - $url is a string of an api call like "statuses/delete/:id" 
   
   - returns the array conversion of the json response
 */
function api_delete($url, $array) {
	global $srv;
	global $token;

	$cSession = curl_init();
	curl_setopt($cSession, CURLOPT_HEADER, false);
	curl_setopt($cSession, CURLOPT_POST, 1);
	curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/" . $url);
	curl_setopt($cSession, CURLOPT_CUSTOMREQUEST, "DELETE");
	if (!is_null($token)) {
		curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
	}
	curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query($array));
	curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
	$result = curl_exec($cSession);
	curl_close($cSession);

	return json_decode($result, true);
}

/* a function to make an authenticated general PATCH api call to the logged-in instance.
   - $url is a string of an api call like "account/:id/statuses" 
   
   - returns the array conversion of the json response
 */
function api_patch($url, $array) {
	global $srv;
	global $token;

	$cSession = curl_init();
	curl_setopt($cSession, CURLOPT_HEADER, false);
	curl_setopt($cSession, CURLOPT_POST, 1);
	curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/" . $url);
	curl_setopt($cSession, CURLOPT_CUSTOMREQUEST, "PATCH");
	if (!is_null($token)) {
		curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
	}
	curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query($array));
	curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
	$result = curl_exec($cSession);
	curl_close($cSession);

	return json_decode($result, true);
}

/* Function to upload files to the profile of the authenticated user
   - $type can be 'avatar' or 'header'
   
   - returns the json of the api call
   */
function upload_profile($file,$type){
	global $srv;
	global $token;
	
	$mime = get_mime($file);
	$info = pathinfo($file);
	$name = $info['basename'];
	$output = new CURLFile($file, $mime, $name);
		$cSession = curl_init();
		curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/accounts/update_credentials");
		curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($cSession, CURLOPT_POST, 1);
		curl_setopt($cSession, CURLOPT_CUSTOMREQUEST, "PATCH");
		curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
		curl_setopt($cSession, CURLOPT_POSTFIELDS, array(
			$type => $output
		));
		$result = curl_exec($cSession);
		curl_close($cSession);
		return $result;
}

/* this function fetches all the data from a profile (bio, username, relationships, etc) 
	- $user is the id of the queried user
	
	- returns the array conversion of the json response
*/
function user_info($user) {
	global $user_settings;
	$info = api_get("accounts/" . $user);
	$rel = api_get("accounts/relationships?id=" . $user);
	return array(
		$info,
		$rel
	);
}

/* this function fetches the context (the previous posts and replies) of a specified post 
   - $post = ID of the post queried.
   
   - returns an array conversion of the json response
*/
function context($post) {
	global $srv;
	global $token;

	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$post/context");
	if (!is_null($token)) {
		curl_setopt($curl, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
	}
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	$result = curl_exec($curl);
	curl_close($curl);
	return $result;
}


/* a function to fav or unfav a specified post 
	- $post = ID of the post queried.
	- $mode can be true if is a fav or false if it's an unfav
	
	- returns the number of favs of the post after the specified action or "error"
	  if there was an error performing the action
*/
function favourite($post, $mode) {
	$result = api_post(($mode == true ? "statuses/$post/favourite" : "statuses/$post/unfavourite"),array());

	if (isset($result['favourites_count'])) {
		return $result['favourites_count'];
	}
	else {
		return "error";
	}
}


/* a function to reblog or unreblog a specified post 
	- $post = ID of the post queried.
	- $mode can be true if is a reblog or false if it's an unreblog
	
	- returns the number of reblogs of the post after the specified action or "error"
	  if there was an error performing the action
*/
function reblog($post, $mode) {
	$result = api_post(($mode == true ? "statuses/$post/reblog" : "statuses/$post/unreblog"),array());
	if (isset($result['reblog']['reblogs_count'])) {
		return $result['reblog']['reblogs_count'];
	}
	elseif (isset($result['reblogs_count'])) {
		return $result['reblogs_count'];
	}
	else {
		return "error";
	}
}



/* function to delete a post 
   - $id is the id of the post to delete
   
   - returns 1 if the post was deleted succesfully or 0 if there was an error
*/
function delpost($id) {
	global $srv;
	global $token;

	if (!is_null($token)) {
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_HEADER, false);
		curl_setopt($curl, CURLOPT_POST, 1);
		curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$id");
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
		curl_setopt($curl, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));

		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		$result = curl_exec($curl);
		curl_close($curl);
		$result = json_decode($result, true);
		if (empty($result)) {
			return "1";
		}
		else {
			return "0";
		}
	}
}

/* function to issue a vote to a poll
	- $id is the id of the poll to vote on
	
	- $choices is a comma separated string of the choices that will be voted.
*/
function vote($poll, $choices) {
	global $srv;
	global $token;
	if (!is_null($token)) {
		$cSession = curl_init();
		curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/polls/$poll/votes");
		curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($cSession, CURLOPT_POST, 1);
		curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
		$query = "";
		$choicelist = explode(",",$choices);
		foreach($choicelist as $choice){
			$query .= "choices[]=$choice&";
		}
		
		curl_setopt($cSession, CURLOPT_POSTFIELDS, $query);
		$result = curl_exec($cSession);
		curl_close($cSession);
		return $result;
	}
	else {
		return false;
	}
}


/* function to send a new post to the logged in instance
	- $text = the body of the message
	- $media = array of uploaded media id's
	- $reply = the id of a post being replied to, can be null.
	- $markdown = specify if the post uses markdown (unused at this moment)
	- $scope = the scope of the post (public, private, unlisted or direct)
	- $sensitive = bool to specify if the post media is sensitive
	- $spoiler = string of the title of the post
	
	- returns the json of the api response
   */
function sendpost($text, $media, $reply = null, $markdown = false, $scope = "public", $sensitive = false, $spoiler = false) {
	global $srv;
	global $token;
	if (!is_null($token)) {

		$cSession = curl_init();
		curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/statuses");
		curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($cSession, CURLOPT_POST, 1);
		curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $token
		));
		$query = "";
		$query .= "status=" . urlencode(html_entity_decode($text, ENT_QUOTES)) . "&visibility=" . $scope;;
		if (!is_null($reply) && $reply != "null") {
			$query .= "&in_reply_to_id=" . $reply;
		}
		if ($markdown == true) {
			$query .= "&content_type=text/markdown";
		}
		if ($sensitive == 'true') {
			$query .= "&sensitive=true";
		}
		if ($spoiler == true) {
			$query .= "&spoiler_text=" . $spoiler;
		}

		if (!is_null($media)) {
			foreach ($media as $mid) {
				$query .= "&media_ids[]=" . $mid;
			}
		}
		
		curl_setopt($cSession, CURLOPT_POSTFIELDS, $query);
		$result = curl_exec($cSession);
		curl_close($cSession);
		return $result;
	}
	else {
		return false;
	}
}

/* uploads a file to the logged in instance.
	- $file = path of the file to upload on local storage
	
	- returns an array where: 
	    0: the ID of the uploaded file
		1: the url of the file (if the file isn't an image, returns a placeholder)
   */
function uploadpic($file) {
	global $srv;
	global $token;
	if (!is_null($token)) {
		$mime = get_mime($file);
		$info = pathinfo($file);
		$name = $info['basename'];
		$output = new CURLFile($file, $mime, $name);
			$cSession = curl_init();
			curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/media");
			curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($cSession, CURLOPT_POST, 1);
			curl_setopt($cSession, CURLOPT_HTTPHEADER, array(
				'Authorization: Bearer ' . $token
			));
			curl_setopt($cSession, CURLOPT_POSTFIELDS, array(
				'file' => $output
			));
			$result = curl_exec($cSession);
		curl_close($cSession);
		$array = json_decode($result, true);

		$ext = explode(".", $array['url']);
		$ext = end($ext);
		$ext = explode("?", $ext) [0];

		if (in_array($ext, array(
			'jpg',
			'jpeg',
			'gif',
			'png',
			'svg'
		))) {
			$file = $array['url'];
		}
		else {
			$file = "img/doc.png";
		}
		return json_encode(array(
			$array['id'],
			$file
		));
	}
	else {
		return false;
	}
}

/* this is used to register DashFE as an application on an instance 
   mostly used when logging in
   - $instance = the url of the instance where to log in
   
   - returns the array conversion of the json response
 */
function register_app($instance) {
	global $setting;

	$cSession = curl_init();
	curl_setopt($cSession, CURLOPT_URL, "https://$instance/api/v1/apps");
	curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($cSession, CURLOPT_HEADER, false);
	curl_setopt($cSession, CURLOPT_POST, 1);
	curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query(array(
		'client_name' => $setting['appname'],
		'redirect_uris' => $setting['url'],
		'scopes' => 'read write follow'
	)));
	$result = curl_exec($cSession);
	curl_close($cSession);

	return json_decode($result, true);
}

/* this function will get all the notes (reblogs and favs) that has an specified post
	- $thread = the id of the post queried
	
	- returns an array with all the notes
	  each note is another array where:
	  0 = type of note ("reb" reblog or "fab" favorite)
	  1 = array of the details of the user, converted from the json of the api response.
 */ 
function getnotes($thread) {
	global $user_settings;
	global $token;
	global $srv;
	global $setting;
	global $logedin;
	@$reb = array(
		api_get("statuses/" . $thread . "/reblogged_by")
	);
	@$fab = array(
		api_get("statuses/" . $thread . "/favourited_by")
	);
	$limit = (count($reb[0]) > count($fab[0]) ? count($reb[0]) - 1 : count($fab[0]) - 1);

	$notes = array();
	$index = 0;

	for ($i = 0;$i <= $limit;$i++) {
		if (isset($reb[0][$i])) {
			$notes[$index][0] = "reb";
			$notes[$index][1] = $reb[0][$i];
			$index++;
		}
		if (isset($fab[0][$i])) {
			$notes[$index][0] = "fav";
			$notes[$index][1] = $fab[0][$i];
			$index++;
		}
	}

	return $notes;
}

/* this function will fetch replies of a post
	- $thread = the id of the post from where to fetch replies
	- $since = id of a post. If specified, the function will fetch the replies 
	           only since the specified id.
			   
	- returns an array with all the replies
	  each element is another array where:
	  'mode' = type of the reply (ancestor or descendant)
	  'content' = array of the contents of the reply, converted from the json of the api response.
 */ 
function getreplies($thread, $since = false) {
	global $user_settings;
	global $token;
	global $srv;
	global $setting;
	global $logedin;
	$context = json_decode(context($thread) , true);
	$array = array();
	if (!empty($context['ancestors'])) {
		if ($since == false) {
			foreach ($context['ancestors'] as $elem) {
				$elem['type'] = 'ancestor';
				$array[] = $elem;
			}
		}
	}
	$flag = 0;
	if (!empty($context['descendants'])) {
		foreach ($context['descendants'] as $elem) {
			if (($since != false && $flag == 1) || $since == false) {
				$elem['type'] = 'descendant';
				$array[] = $elem;
			}
			if ($since != false && $elem['id'] == $since) {
				$flag = 1;
			}
		}
	}

	$replies = array();

	foreach ($array as $item) {
		$reply['mode'] = "";
		if ($item['type'] == 'ancestor') {
			$reply['mode'] = "ancestor";
		}

		$replies[] = array(
			'mode' => $reply['mode'],
			'content' => $item
		);
	}

	return $replies;
}


/* this function takes some options from the init.php file and the user_settings cookie 
   and fetches all the posts of the appropiate timeline 
   - $query = an array with a set of elements generated by the file "include/init.php" 
   
   - returns an array of the posts list fetched converted from the json response of the api.  
   */
function timeline($query) {
	global $token;
	global $srv;

	$notes = "";
	
	$hq = array();
	$hq['limit'] = 10;
	$hq['only_media'] = ($query['text'] == "off" ? 'true' : 'false');
	if ($query['next']){
		$hq['max_id'] = $query['next'];
		$next = $query['next'];
	} elseif ($query['since']) {
		$hq['since_id'] = $query['since'];
	}
	
	switch ($query['mode']) {
		case "home":
			$array = api_get("timelines/home?".http_build_query($hq));
		break;

		case "federated":
			$array = api_get("timelines/public?".http_build_query($hq));
		break;

		case "tag":
			$array = api_get("timelines/tag/" . $query['tag'] . "?".http_build_query($hq));
		break;

		case "local":
			$array = api_get("timelines/public?local=true&".http_build_query($hq));
		break;

		case "user":
			$array = api_get("accounts/" . $query['user'] . "/statuses?".http_build_query($hq));
		break;

		case "thread":
			$array = array(
				api_get("statuses/" . $query['thread'])
			);
		break;

		case "favourites":
			$array = api_get("favourites?".http_build_query($hq));
		break;

		case "direct":
			$array = api_get("timelines/direct?".http_build_query($hq));
		break;

		case "list":
			$array = api_get("timelines/list/" . $query['list'] . "?".http_build_query($hq));
		break;

		case "bookmarks":
			$array = api_get("bookmarks?".http_build_query($hq));
		break;
		
		case "search":
			$array = api_getv2("search?limit=40&q=".$query['search']."{$next}")['statuses'];
		break;

		case "account":
			$info = api_get("accounts/verify_credentials");
			$array = api_get("accounts/" . $info['id'] . "/statuses?".http_build_query($hq));
		break;

		default:
			$array = api_get("timelines/public?".http_build_query($hq));
		break;
	}

	if (!is_array($array)) {
		return false;
	}

	$next = end($array) ['id'];
	$thread = array();
	/*
	foreach ($array as $elem) {
		if ($query['replies'] == "on" || $query['mode'] == "thread") {
			$thread[] = $elem;
		}
		else {
			if ($elem['in_reply_to_id'] == null) {
				$thread[] = $elem;
			}
		}

	}*/
 
	foreach ($array as $elem) {
		if ($query['replies'] == "on" || $query['mode'] == "thread") {
			$thread[] = $elem;
		}
		else {
			if ($elem['in_reply_to_id'] == null || $elem['in_reply_to_account_id'] == $query['uid']) {
				$thread[] = $elem;
			} else {
				$rel = api_get("accounts/relationships?id=" . $elem['in_reply_to_account_id']);
				if ($rel[0]['following']){
					$thread[] = $elem;
				}
			}
		}

	}

	return $thread;
}










// FUNCTIONS THAT HAVE TO DO WITH RENDERING STUFF FOR THE PAGE

/* this function is used to generate the html code of a poll */
function renderPoll($elem) {
	global $logedin;
	
	$output = "";
	$output .= "<br>";
	$votes = $elem['poll']['votes_count'];
	
	if ($elem['poll']['voted'] || $elem['poll']['expired']) {
		$output.= "<b>Votes: $votes</b><br>";
		foreach ($elem['poll']['options'] as $option){
			$percentage = ($option['votes_count'] / $votes ) * 100;
			$output .= "<div class='polloption fixed' title='".$option['votes_count']." votes'><div class='voteBar' style='font-weight:bold; max-width:".$percentage."%;padding:1px; height:10px;'> </div>".$option['title']."</div>";
		}
	} else {
		foreach ($elem['poll']['options'] as $option){
			$output .= "<div class='polloption'>".$option['title']."</div>";
		}
		$output .= ($logedin ? "<input type='submit' class='vote' id='".$elem['poll']['id']."' value='Send Vote' style='padding:2px;' onClick='return false;'>" : "");
	}
	return $output;
}


/* this function is used to generate the html code of a reply */
function render_reply($item) {
	global $user_settings;
	global $logedin;
	global $srv;

	$reply['mode'] = "";
	if (isset($item['type']) && $item['type'] == 'ancestor') {
		$reply['mode'] = "ancestor";
	}

	$reply['id'] = $item['id'];
	$reply['uid'] = $item['account']['id'];
	$reply['name'] = emojify($item['account']['display_name'], $item['account']['emojis'], 20);
	$reply['acct'] = $item['account']['acct'];
	$reply['handle'] = "@".explode("@",$item['account']['acct'])[0];
	$reply['avatar'] = $item['account']['avatar'];

	$reply['menu'] = "<ul>";
	if ($logedin) {
		$reply['menu'] .= ($item['account']['id'] == $user_settings['uid'] ? "<li><a href='?action=delete&thread=" . $item['id'] . "' onClick='return false;' class='delete fontello' id=':id:'>&#xe80e; Delete Post</a></li>" : "");
		$reply['menu'] .= "<li><a href='?action=compose&quote=" . $item['id'] . "' onClick='return false;' class='quote fontello' id='" . $item['id'] . "' style='background-color:transparent;'>&#xf10e; Quote Post</a></li>";
		$reply['menu'] .= ($item['account']['id'] != $user_settings['uid'] ? "<li><a href='?action=mute&user=" . $item['account']['id'] . "' onClick='return false;' class='mute fontello' id='" . $item['account']['id'] . "' style='background-color:transparent;'>&#xe81b; Mute User</a></li>" : "");
		$reply['menu'] .= ($item['account']['id'] != $user_settings['uid'] ? "<li><a href='?action=mute&thread=" . $item['account']['id'] . "' onClick='return false;' class='muteconv fontello' id='" . $item['id'] . "' style='background-color:transparent;'>&#xf1f7; Drop Thread</a></li>" : "");
		$reply['menu'] .= (isset($user_settings['pleroma']) ? "<li><a href='?action=hide&thread=" . $item['pleroma']['conversation_id'] . "' onClick='return false;' class='hide fontello' id='" . $item['pleroma']['conversation_id'] . "' style='background-color:transparent;'>&#xf1f8; Hide Thread</a></li>" : "");
		$reply['menu'] .= (isset($user_settings['pleroma']) ? "<li><a href='?action=bookmark&thread=" . $item['account']['id'] . "' onClick='return false;' class='" . ($item['bookmarked'] == true ? "un" : "") . "bookmark fontello' id='" . $item['id'] . "' style='background-color:transparent;'>&#xe81e; " . ($item['bookmarked'] == true ? "Unb" : "B") . "ookmark</a></li>" : "");
		$reply['menu'] .= ($item['account']['id'] != $user_settings['uid'] ? "<li><a href='?action=nsfw&user=" . $item['account']['id'] . "' onClick='return false;' class='nsfw fontello' id='" . $item['account']['id'] . "' style='background-color:transparent;'>&#xe829; User is NSFW</a></li>" : "");
	}
	$reply['menu'] .= "<li><a target='_blank' href='" . $item['url'] . "' class='original link fontello'  style='background-color:transparent;'>&#xf14c; Original Note</a></li>";
	$reply['menu'] .= "</ul>";

	$json['id'] = $item['id'];
	$json['scope'] = $item['visibility'];
	if ($logedin) {
		$json['mentions'] = "";
		$array = $item["mentions"];
		$json['mentions'] = ($user_settings['acct'] == $item["account"]['acct'] ? "" : "@" . $item["account"]['acct']) . " ";
		if (!empty($array)) {
			foreach ($array as $mnt) {
				if ($mnt['acct'] != $user_settings['acct']) {
					$json['mentions'] .= "@" . $mnt['acct'] . " ";
				}
			}
		}
	}
	$reply['json'] = json_encode($json);

	$reply['replyto'] = ($item['in_reply_to_id'] ? " <a class='fontello link preview ldr' target='_blank' id='" . $item['in_reply_to_id'] . "' href='?thread=" . $item['in_reply_to_id'] . "'>&#xf112;</a> " : "");
	$reply['text'] = processText($item);

	$reply['date'] = "<a class='ldr postAge' id='".strtotime($item['created_at'])."' style='text-decoration:none;' target='_blank' href='?thread=" . $item['id'] . "'>" . time_elapsed_string($item['created_at']) . "</a>";
	$reply['visibility'] = $item['visibility'];

	$reply['media'] = "";
	if (!empty($item['media_attachments'])) {
		$reply['media'] = "<div style='width:170px; display:inline-block; float:left; margin:15px 0px 10px 0px;'>";
		$images = count($item['media_attachments']);
		$class = ($images > 1 ? "class='icon'" : "");
		foreach ($item['media_attachments'] as $file) {
			$ext = explode(".", $file['url']);
			$ext = end($ext);
			$ext = explode("?", $ext) [0];
			if (in_array($ext,array('webm','mp4','ogv'))) {
				$reply['media'] .= "<div style='text-align:center; width:100%;'><video preload='metadata' width='100%' controls ".($user_settings['videoloop'] == "on" ? "loop" : "").">
							<source src='" . $file['url'] . "' type='video/".($ext == "ogv" ? "ogg" : $ext)."'>
							</video></div>
						";
			}
			elseif (in_array($ext,array('mp3','ogg','oga','opus'))) {
				$reply['media'] .= "<div style='text-align:center; width:100%;'><audio controls>
												<source src='" . $file['url'] . "' type='audio/$ext'>
												Your browser does not support the audio tag.
											</audio> </div>";
			}
			else {
				if ($item['sensitive'] == true && $user_settings['explicit'] != 'off') {
					$reply['media'] .= "<div style='overflow:hidden; float:left; margin:2px;' $class><a target='_blank' href='" . $file['url'] . "' onClick='return false;' class='blur'><noscript><img src='" . $file['url'] . "' style='width:100%;'></noscript><img " . "data-src='" . $file['url'] . "'" . " class='' style='max-width:100%; max-height:100% vertical-align:middle;'></a><a target='_blank' href='" . $file['url'] . "' onClick='return false;' class='open-lightbox' style='display:none;'><img src='" . $file['url'] . "' class='' style='width:100%;'></a></div>";
				}
				else {
					$reply['media'] .= "<div style='margin:0px;' $class><a target='_blank' href='" . $file['url'] . "' onClick='return false;' class='open-lightbox'><img src='" . $file['url'] . "'" . " class='' style='max-width:100%; max-height:100%; vertical-align:middle;'><noscript><img src='" . $file['url'] . "' style='width:100%;'></noscript></a></div>";

				}
			}
		}
		$reply['media'] .= "</div>";
	}

	$reply['buttons'] = "
		" . ($logedin ? "<div class='felem'><a onClick='return false' class='replyform' href='?thread=" . $item['id'] . "' style='font-family:fontello'>&#xf112;</a></div>" : "") . "
					<div class='felem'><a onClick='return false' " . ($logedin ? "class='" . ($item['favourited'] == true ? "unfav" : "fav") . "' href='?action=fav&thread=" . $item['id'] . "'" : "") . " style='font-family:fontello'>&#xe802; <span>" . $item['favourites_count'] . "</span></a></div>
					<div class='felem'><a onClick='return false' " . ($logedin && ($item['visibility'] != "private" || $item['visibility'] != "direct") ? "class='" . ($item['reblogged'] == true ? "unreblog" : "reblog") . "' href='?action=reblog&thread=" . $item['id'] . "'" : "") . " style='font-family:fontello'>&#xe83a; <span>" . $item['reblogs_count'] . "</span></a></div>
		";

	$result = themes("get","templates/reply.txt");

	foreach ($reply as $key => $elem) {
		$result = str_replace(":$key:", $elem, $result);
	}

	return $result;
}

/* this is the same as above but is used on other places, like user bios and places where the 
shortcodes and the emoji url is defined in the same place */
function emojify($string, $emojis, $size = 40) {
	foreach ($emojis as $emoji) {
		$string = str_replace(":" . $emoji['shortcode'] . ":", "<img class='emoji' alt='" . $emoji['shortcode'] . "' title='" . $emoji['shortcode'] . "' src='" . $emoji['static_url'] . "' height=$size style='vertical-align: middle;'>", $string);
	}
	return $string;
}

/* This function displays the emoji list of an instance based on a search 
   string given on $val */
function emoji_list($val){
	$emojilist = api_get("/custom_emojis");
	$c = 0;
	$return = "";
	foreach ($emojilist as $emoji){
		if (starts_with($emoji['shortcode'],$val) && $c < 50){
			$return .= "<img style='margin:1px;' src='".$emoji['static_url']."' class='emoji' title='".$emoji['shortcode']."' height=40>";
			$c++;
		}
	}
	if ($c < 50){
		foreach ($emojilist as $emoji){
			if ((contains($emoji['shortcode'],$val) && !starts_with($emoji['shortcode'],$val)) && $c < 50){
				$return .= "<img style='margin:1px;' src='".$emoji['static_url']."' class='emoji' title='".$emoji['shortcode']."' height=40>";
				$c++;
			}
		}
	}
	
	return $return;
}

function contact_search($val){
	global $user_settings;
	$return = "";
	$list = api_get("/accounts/search?q=".$val);
	foreach ($list as $contact){
		$return .= "<div class='contact' title='@".$contact['acct']."' style='width:100%; clear:both; height:40px; display:inline-block;'>
			<div style='width:40px; height:40px; background-size:cover; background-image:url(".$contact['avatar']."); float:left;'></div>
			<div>
				<span style='font-weight:bold;'>".emojify($contact['display_name'], $contact['emojis'], 15)."</span><br>
				<span style='font-size:12px;'>".$contact['acct']."</span>
			</div>
		</div>";
	}
	return $return;
}

/* This function will fetch and render all the notifications since an $id
   or get a list of all past notification ($max notifications specified)
   */
function getnotif($id = false, $max = false) {
	global $srv;
	global $token;
	global $user_settings;
	$n = "";
	$notif = api_get("notifications?" . ($id == false ? "limit=9&" : "") . ($id != false ? ($max == true ? "max_id=$id" : "since_id=$id") : ""));
	if (!empty($notif)) {
		foreach ($notif as $post) {
			if (!isset($post["type"])){
				break;
			}
			$user = "<a class='link ldr uname' style='font-size:12px;' href='?user=" . $post['account']['id'] . "'>" . (empty($post['account']['display_name']) ? $post['account']['acct'] : emojify($post['account']['display_name'], $post['account']['emojis'], 10)) . "</a>";
			$preview = "";
			$buttons = "";
			$media = "";

			if ($post["type"] == "pleroma:emoji_reaction"){
				continue;
			}

			switch ($post["type"]) {
				case "mention":
					if ($post['status']['in_reply_to_id'] == null) {
						$type = "<span class='fontello' style='color:#62C2CC; font-size:10px;'>&#xf10d;</span>";
						$string = "mentioned you in a post";
						$preview = "<a style='text-decoration:none;' class='ldr text' href='?thread=" . $post['status']['id'] . "' target='_blank'><span style='display:block; opacity:1; font-size:10px; line-height:12px;'>" . emojify(strip_tags(trim($post['status']['content']) , '<br><br \>') , $post['status']['emojis'], 15) . "</span></a>";
						$media = (!empty($post['status']['media_attachments']) ? "<a style='text-decoration:none;' class='ldr' href='?thread=" . $post['status']['id'] . "' target='_blank'><div class='notifpic' style='flex: 0 0 60px; background-size:cover; background-image:url(" . $post['status']['media_attachments'][0]['url'] . ");'></div></a>" : "");
					}
					else {
						$type = "<span class='fontello' style='color:#62C2CC; font-size:10px;'>&#xf112;</span>";
						$string = "replied to your post";
						$preview = "<a style='text-decoration:none;' class='ldr text' href='?thread=" . $post['status']['id'] . "' target='_blank'><span style='display:block; opacity:1; font-size:10px; line-height:12px;'>" . emojify(strip_tags(trim($post['status']['content']) , '<br><br \>') , $post['status']['emojis'], 15) . "</span></a>";
						$media = (!empty($post['status']['media_attachments']) ? "<div class='notifpic' style='flex: 0 0 60px; background-size:cover; background-image:url(" . $post['status']['media_attachments'][0]['url'] . ");'></div>" : "");
					}

					$array = $post['status']["mentions"];
					$mentions = ($user_settings['acct'] == $post['status']['account']['acct'] ? "" : "@" . $post['status']['account']['acct']) . " ";
					if (!empty($array)) {
						foreach ($array as $mnt) {
							if ($mnt['acct'] != $user_settings['acct']) {
								$mentions .= "@" . $mnt['acct'] . " ";
							}
						}
					}

					$buttons = "<div class='post_buttons' id='" . $post['status']['id'] . "' style='position:absolute; right:10px; bottom:10px; border-radius:60px; padding:5px;'>
					<div class='felem'><a onClick='return false' class='quickreply' id='" . $post['status']['id'] . "' data-mentions='" . $mentions . "' href='?thread=" . $post['status']['id'] . "' style='font-family:fontello'>&#xe824;</a></div>
					<div class='felem'><a onClick='return false' class='" . ($post['status']['favourited'] == true ? "unfav" : "fav") . "' href='?action=fav&thread=" . $post['status']['id'] . "'" . " style='font-family:fontello'>&#xe802;</a></div>
					<div class='felem'><a onClick='return false' " . ($post['status']['visibility'] == "public" || $post['status']['visibility'] == "unlisted" ? "class='" . ($post['status']['reblogged'] == true ? "unreblog" : "reblog") . "' href='?action=reblog&thread=" . $post['status']['id'] . "'" : "") . " style='font-family:fontello'>&#xe83a;</a></div></div>";
				break;

				case "favourite":
					$type = "<span class='fontello' style='color:#F17022; font-size:10px;'>&#xe802;</span>";
					$string = "favourited your post";
					$preview = "<a style='text-decoration:none;' class='ldr text' href='?thread=" . $post['status']['id'] . "' target='_blank'><span style='display:block; opacity:1; font-size:10px; line-height:12px;'>" . (!empty($post['status']['content']) ? emojify(strip_tags(trim($post['status']['content']) , '<br><br \>') , $post['status']['emojis'], 15) : "Favourited your image") . "</span></a>";
					$media = (!empty($post['status']['media_attachments']) ? "<div class='notifpic'  style='flex: 0 0 60px; background-size:cover; background-image:url(" . $post['status']['media_attachments'][0]['url'] . ");'></div>" : "");
				break;

				case "reblog":
					$type = "<span class='fontello' style='color:#D1DC29; font-size:10px;'>&#xe826;</span>";
					$string = "reblogged your post";
					$preview = "<a style='text-decoration:none;' class='ldr text' href='?thread=" . $post['status']['id'] . "' target='_blank'><span style='display:block; opacity:1; font-size:10px; line-height:12px;'>" . (!empty($post['status']['content']) ? emojify(strip_tags(trim($post['status']['content']) , '<br><br \>') , $post['status']['emojis'], 15) : "Reblogged your image") . "</span></a>";
					@$media = (!is_null($post['status']['media_attachments']) ? "<div class='notifpic'  style='flex: 0 0 60px; background-size:cover; background-image:url(" . $post['status']['media_attachments'][0]['url'] . ");'></div>" : "");
				break;

				case "follow":
					list($info, $rel) = user_info($post["account"]["id"]);
					$type = "<span class='fontello' style='color:#FDB813; font-size;10px;'>&#xf234;</span>";
					$preview = "started following you";
					if ($rel[0]['following']) {
						$label = "&#xe80c; Following";
						$class = "unfollow";
					}
					else {
						if ($info['locked']) {
							if ($rel[0]['requested']) {
								$label = "&#xe806; 	Follow Requested";
								$class = "unfollow";
							}
							else {
								$label = "&#xe806; Request Follow";
								$class = "follow";
							}
						}
						else {
							$label = "&#xf234; Follow";
							$class = "follow";
						}
					}
					$buttons .= "<div class='post_buttons' style='position:absolute; right:10px; bottom:10px; border-radius:60px; padding:5px;'><span id='" . $info['id'] . "' class='profileButton $class' style='background-color:white; font-family:sans,fontello ;font-size:13px;'>$label</span></div>";
				break;
			}

			$n .= "
			<div class='notif " . ($id == false ? "" : "new") . "' id='" . $post['id'] . "'>
				<div class='notifContents'>
					<div style='flex: 0 0 60px; background-size:cover; background-image:url(" . $post['account']['avatar'] . "); border-radius:5px;'></div>
					<div style='flex: 1; padding-left:5px; padding-right:5px; word-break: break-word; overflow:hidden;'>
					<span>$type <span style='font-size:12px; font-weight:bold;'>$user</span></span>
					" . trim($preview) . "
					$buttons
					</div>
					$media
				</div>
			</div>
			";

		}
		return $n;
	}
}

/* this function takes in a whole post entity and spits out the HTMLfied text of the post
   with the urls and @handles linkified and all the emojis replaced */

function processText($elem) {
	global $user_settings;
	global $logedin;
	require_once "vendor/simple_html_dom.php";
	$content = trim(html_entity_decode($elem['content'],ENT_QUOTES));
	$content = preg_replace("/<(?=[^>]*(?:<|$))/","&lt;",$content);
	
	if (!empty($content)) {
		$html = str_get_html($content);
		foreach ($html->find('a') as $lnk) {
			
			//remove text links to media attachments
			foreach ($elem['media_attachments'] as $f) {
				if (is_numeric(strpos($f['description'],explode("…",$lnk->innertext)[0]))) {
					$content = str_replace($lnk->outertext . "<br/>", null, $content);
					$content = str_replace("<br/>" . $lnk->outertext, null, $content);
					$content = str_replace($lnk->outertext, null, $content);
				}
			}
			
			//modify links for hashtags and external urls
			if (is_numeric(strpos($lnk->href, $user_settings['instance'])) || in_array($lnk->class, array(
				"u-url mention",
				"hashtag"
			)) || $lnk->rel == "tag") {
				$content = str_replace($lnk->outertext, $lnk->innertext, $content);
			}
			else {
				$prv = $lnk->outertext;
				$lnk->target = '_blank';
				$lnk->class = 'link external';
				$content = str_replace($prv, $lnk->outertext, $content);
			}
		}
	}
	
	$result = strip_tags($content, '<br><p><strong><a><em><strike>');
	$result = str_replace('<br />', ' <br>', $result);
	//$result = str_replace('&#39;', "'", $result);

	foreach ($elem['mentions'] as $mention) {
		$result = str_replace("@" . $mention['username'], "<span class='user' id='" . $mention['id'] . "'><a href='?user=" . $mention['id'] . "' class='link ldr' onClick='return false;'>@" . $mention['username'] . "</a></span>", $result);
	}

	//$result = preg_replace("/(?<!=\")(\b[\w]+:\/\/[\w-?&;#~=\.\/\@]+[\w\/])/", "<a target=\"_blank\" class='external' style='color:navy;' href=\"$1\">$1</a>", $result);
	$result = emojify($result, $elem['emojis']);
	if ($logedin){
		$result = preg_replace("/#([A-Za-z0-9\/\.]*)/", "<a class='ldr' href=\"./?tag=$1\"><b>#$1</b></a>", $result);
	} else {
		$result = preg_replace("/#([A-Za-z0-9\/\.]*)/", "<b>#$1</b>", $result);
	}

	return $result;
}














// OTHER FUNCTIONS

/* the purpose of this function is to encode the auth token
   it is not used for now */
function msc($string, $action = 'e') {
	// you may change these values to your own
	$secret_key = 'yAmfVhZwm0749FSY24dC';
	$secret_iv = 'm37uvAeKjYLKdI1lPkcJ';

	$output = false;
	$encrypt_method = "AES-256-CBC";
	$key = hash('sha256', $secret_key);
	$iv = substr(hash('sha256', $secret_iv) , 0, 16);

	if ($action == 'e') {
		$output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv));
	}
	else if ($action == 'd') {
		$output = openssl_decrypt(base64_decode($string) , $encrypt_method, $key, 0, $iv);
	}

	return $output;
}

/* this function extracts the urls from a text and return them in an array */
function get_urls($input) {
	$pattern = '$(https?://[a-z0-9_./?=&-~]+)(?![^<>]*>)$i'; 
	if (preg_match_all($pattern, $input, $matches)) {
		list($dummy, $links) = ($matches);
		return $links;
	}
	return false;
}

/* general function to check if one strings starts with a given search */ 	
function starts_with($string,$search){
	if (substr(strtolower($string),0,strlen($search)) == strtolower($search)){
		return true;
	}
	return false;
}

/* general function to check if one strings contains with a given search */ 
function contains($string,$search){
	if (is_numeric(strpos(strtolower($string),strtolower($search)))){
		return true;
	}
	return false;
}

/* this function just reduces an image to a 1x1 pixel image to get the overall color.
   */
function averageColor($url) {
	@$image = imagecreatefromstring(file_get_contents($url));

	if (!$image) {
		$mainColor = "CCCCCC";
	}
	else {
		$thumb = imagecreatetruecolor(1, 1);
		imagecopyresampled($thumb, $image, 0, 0, 0, 0, 1, 1, imagesx($image) , imagesy($image));
		$mainColor = strtoupper(dechex(imagecolorat($thumb, 0, 0)));
	}

	return $mainColor;
}

/* function used in the process of uploading a file */
function get_mime($filename) {
	$result = new finfo();

	if (is_resource($result) === true) {
		return $result->file($filename, FILEINFO_MIME_TYPE);
	}

	return false;
}

function sanitize($text){
	return preg_replace("/[^a-zA-Z0-9]+/", "", $text);
}


function themes($mode,$name = false){
	global $user_settings;
	switch ($mode){
		case "list":
			$themes = scandir("themes/");
			$themelist = array();
			foreach ($themes as $elem){
				if ($elem != ".." && $elem != "." && $elem != "custom" && is_dir("themes/".$elem)){
					$themelist[] = $elem;
				}
			}
			return $themelist;
			
		case "file":
			$theme = sanitize($user_settings['theme']);
			if (file_exists("themes/$theme/$name")){
				return "themes/$theme/$name";
			} else {
				return "$name";
			}
			
		case "get":
			$theme = sanitize($user_settings['theme']);
			if (file_exists("themes/$theme/$name")){
				return file_get_contents("themes/$theme/$name");
			} else {
				return file_get_contents("$name");
			}
	}
}

function time_elapsed_string($datetime, $full = false) {
    $now = new DateTime;
    $ago = new DateTime($datetime);
    $diff = $now->diff($ago);

    $diff->w = floor($diff->d / 7);
    $diff->d -= $diff->w * 7;

    $string = array(
        'y' => 'year',
        'm' => 'month',
        'w' => 'week',
        'd' => 'day',
        'h' => 'hour',
        'i' => 'minute',
        's' => 'second',
    );
    foreach ($string as $k => &$v) {
        if ($diff->$k) {
            $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
        } else {
            unset($string[$k]);
        }
    }

    if (!$full) $string = array_slice($string, 0, 1);
    return $string ? implode(', ', $string) . ' ago' : 'just now';
}

function getHeaders($respHeaders) {
    $headers = array();

    $headerText = substr($respHeaders, 0, strpos($respHeaders, "\r\n\r\n"));

    foreach (explode("\r\n", $headerText) as $i => $line) {
        if ($i === 0) {
            $headers['http_code'] = $line;
        } else {
            list ($key, $value) = explode(': ', $line);

            $headers[$key] = $value;
        }
    }

    return $headerText;
}
